// 版权所有20092010 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 达尔文系统调用。
// 该文件编译为普通Go代码，
// 但它也是mksyscall的输入，
// 解析
// 请注意，有时我们使用小写
// syscall_bsd.go或syscall_unix.go中。

package syscall

import (
	"internal/abi"
	"unsafe"
)

type SockaddrDatalink struct {
	Len    uint8
	Family uint8
	Index  uint16
	Type   uint8
	Nlen   uint8
	Alen   uint8
	Slen   uint8
	Data   [12]int8
	raw    RawSockaddrDatalink
}

// 将“kern.hostname”翻译成[]_C_int{0,1,2,3}。
func nametomib(name string) (mib []_C_int, err error) {
	const siz = unsafe.Sizeof(mib[0])

	// 注意（rsc）：将缓冲区设置为具有
	// 大小CTL_MAXNAME+2，但仅使用CTL_MAXNAME 
	// 作为大小似乎很奇怪。我不知道为什么这里有+2，但是
	// 内核使用+2来实现这个函数。
	// 我担心如果我们不在这里包含+2，内核
	// 将在默认情况下比我们指定的
	// 多写2个字，并且我们将得到内存损坏。
	var buf [CTL_MAXNAME + 2]_C_int
	n := uintptr(CTL_MAXNAME) * siz

	p := (*byte)(unsafe.Pointer(&buf[0]))
	bytes, err := ByteSliceFromString(name)
	if err != nil {
		return nil, err
	}

	// Magic sysctl:“设置”0.3为字符串名
	// 用于读回整数数组形式。
	if err = sysctl([]_C_int{0, 3}, p, &n, &bytes[0], uintptr(len(name))); err != nil {
		return nil, err
	}
	return buf[0 : n/siz], nil
}

func direntIno(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}

func direntReclen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}

func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }

const (
	attrBitMapCount = 5
	attrCmnModtime  = 0x00000400
	attrCmnAcctime  = 0x00001000
)

type attrList struct {
	bitmapCount uint16
	_           uint16
	CommonAttr  uint32
	VolAttr     uint32
	DirAttr     uint32
	FileAttr    uint32
	Forkattr    uint32
}

// sysnb pipe（p*[2]int32）（错误）

func Pipe(p []int) (err error) {
	if len(p) != 2 {
		return EINVAL
	}
	var q [2]int32
	err = pipe(&q)
	p[0] = int(q[0])
	p[1] = int(q[1])
	return
}

func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
	var _p0 unsafe.Pointer
	var bufsize uintptr
	if len(buf) > 0 {
		_p0 = unsafe.Pointer(&buf[0])
		bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
	}
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_getfsstat_trampoline), uintptr(_p0), bufsize, uintptr(flags))
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func libc_getfsstat_trampoline()

// go:cgo\u import\u dynamic libc\u getfsstat getfsstat getfsstat”/usr/lib/libSystem.B.dylib“

func setattrlistTimes(path string, times []Timespec) error {
	_p0, err := BytePtrFromString(path)
	if err != nil {
		return err
	}

	var attrList attrList
	attrList.bitmapCount = attrBitMapCount
	attrList.CommonAttr = attrCmnModtime | attrCmnAcctime

	// order是mtime，atime:Chtimes的相反
	attributes := [2]Timespec{times[1], times[0]}
	const options = 0
	_, _, e1 := syscall6(
		abi.FuncPCABI0(libc_setattrlist_trampoline),
		uintptr(unsafe.Pointer(_p0)),
		uintptr(unsafe.Pointer(&attrList)),
		uintptr(unsafe.Pointer(&attributes)),
		uintptr(unsafe.Sizeof(attributes)),
		uintptr(options),
		0,
	)
	if e1 != 0 {
		return e1
	}
	return nil
}

func libc_setattrlist_trampoline()

// go:cgo:cgo\u import\u dynamic libc\u setattrlist setattrlist setattrlist setattrlist setattrlist”/usr/lib/libSystem.B.dylib”

func utimensat(dirfd int, path string, times *[2]Timespec, flag int) error {
	// Darwin不支持SYS_UTIMENSAT 
	return ENOSYS
}

/*
 * Wrapped
 */

// SYS kill（pid int，signum int，posix int）（error error）

func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) }

/*
 * Exposed directly
 */
// SYS Access（path string，mode uint32）（error error）
// SYS Chdir（path string）（error）
// sys Chflags（path string，flags int）（error error）
// sys Chmod（path string，mode uint32）（error error）
// sys Chown（path string，uid int，gid int）（error error）
// sys Chroot（path string）（error error）
// sys Close（fd int）（error）
// sys Dup（fd int）（nfd int，error error）
// sys Dup2（from int，to int）（error error）
// sys Exchangedata（path1 string，path2 string，options int）（error error）
// sys Fchdir（fd int）（error error）
// sys Fchmod（fd int，mode uint32）（错误）
// sys Fchown（fd int，uid int，gid int）（错误）
// sys Flock（fd int，how int）（错误）
// sys Fpathconf（fd int，name int）（val int，错误）
// Fsync未为os.File.Sync（）调用。请参阅内部/poll/fd_Fsync_darwin.go 
// sys Ftruncate（fd int，length int64）（错误）
// sys Getdtablesize（）（size int）
// sysnb Getpgid（pid int）（pgid int，error error）
// sysnb Getpgrp（）（pgrp int）
// sysnb Getpid（）（pid int）
// sysnb Getppid（）（ppid int）
// sys Getpriority（who int，who int）（prio int，error error）
// sysnb Getrlimit（which int，lim）（错误）错误）错误）错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误：错误（路径字符串，uid int，gid int）（错误）
// sys Link（路径字符串，链接字符串）（错误）
// sys Listen（s int，backlog int）（错误）
// sys Mkdir（路径字符串，模式uint32）（错误）
// sys Mkfifo（路径字符串，模式uint32）（错误）
// sys Mlock（b[]字节）（错误）
// sys Mlockall（flags int）（错误）
// sys Mprotect（b[]字节，prot int）（错误）
// sys Munlock（b[]字节）（错误）
// sys Pathconf（路径字符串，名称int）（val int，error error）
// sys Pread（fd int，p[]byte，offset int64）（n int，error error）
// sys Pwrite（fd int，p[]byte，offset int64）（n int，error）
// sys readdir\u r（dir uintpttr，entry*Dirent，result**Dirent）（res Errno）
// sys Readlink（path string，buf[]byte）（n int，err error）
// sys Rename（from string，to string）（error error error）
// sys Seek（fd int，offset int64，whence int）（newoffset int64，error error）=sys Seek 
// sys Select（n int，r*FdSet，w*FdSet，e*FdSet，timeout*Timeval）（error error）
// sys Setegid（egid int）（error error）
// sysnb Seteuid（euid int）（error）
// sys Setlogin（名称字符串）（error error）
// sysnb Setpgid（pid int，pgid int）（error error）
// sys Setpriority（who int，who int，prio int）（error error）
// sysnb Setregid（rgid int，egid int）（error error）
// sysnb Setreuid（ruid int，euid int）（error error）
// sysnb Setrlimit（which int，lim*Rlimit）（error error）
// sysnb setid（）（pid int，error error）
// sys Sync（）（错误）
// sys Truncate（路径字符串，长度int64）（错误）
// sys Umask（新掩码int）（旧掩码int）
// sys Undelete（路径字符串）（错误）
// sys Unlink（路径字符串）（错误）
// sys Unmount（路径字符串，标志int）（错误）
// sys write（fd int，p[]字节）（n int，错误）
// sys writev（fd int，iovecs[]Iovec）（cnt uintpttr，error error）
// sys munmap（addr uintpttr，length uintpttr）（错误）
// sysnb fork（）（pid int，error error）
// sysnb ioctl（fd int，req int，arg int）（错误）
// sysnb ioctlPtr（fd int，req uint，arg unsafe.Pointer）（错误）=sys ioctl 
// sysnb execve（path*byte，argv**byte，envp**byte）（err error）
// sysnb exit（res int）（error error）
// sys sysctl（mib[]\u C_int，old*byte，oldlen*uintptr，new*byte，newlen uintptpr）（error）
// sys openat（fd int，path string，flags int，perm uint32）（fdret int，error error）
// sys getcwd（buf[]字节）（n int，error error）

func init() {
	execveDarwin = execve
}

func fdopendir(fd int) (dir uintptr, err error) {
	r0, _, e1 := syscallPtr(abi.FuncPCABI0(libc_fdopendir_trampoline), uintptr(fd), 0, 0)
	dir = uintptr(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func libc_fdopendir_trampoline()

// go:cgo_import_dynamic libc_fdopendir fdopendir fdopendir/usr/lib/libSystem.B.dylib

func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
	n = int(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := syscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
	n = int(r0)
	if e1 != 0 {
		err = errnoErr(e1)
	}
	return
}

func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
	// 使用fdopendir/readdir\r/closedir模拟Getdirentries.
	// 我们在fd的seek中存储要跳过的条目数
	// 偏移量。请参阅问题#31368.
	// 这不是所需的全部语义，但应该处理调用Getdirentries或ReaD重复读取。
	// 它不会处理将lseek的结果分配给*basep，也不会处理
	// 正在脚下编辑的目录。
	skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
	if err != nil {
		return 0, err
	}

	// 我们需要复制传入的文件描述符
	// 因为调用方希望保留对它的控制，但
	// fdopendir希望控制其参数。
	// 仅仅复制文件描述符是不够的，因为
	// 结果共享底层状态。使用openat创建一个真正的
	// 引用同一目录的新文件描述符。
	fd2, err := openat(fd, ".", O_RDONLY, 0)
	if err != nil {
		return 0, err
	}
	d, err := fdopendir(fd2)
	if err != nil {
		Close(fd2)
		return 0, err
	}
	defer closedir(d)

	var cnt int64
	for {
		var entry Dirent
		var entryp *Dirent
		e := readdir_r(d, &entry, &entryp)
		if e != 0 {
			return n, errnoErr(e)
		}
		if entryp == nil {
			break
		}
		if skip > 0 {
			skip--
			cnt++
			continue
		}
		reclen := int(entry.Reclen)
		if reclen > len(buf) {
			// 空间不足。暂时返回。
			//  /计数器将让我们知道在哪里我们应该再次启动。ABCFDG 
			break
		}
		// 将条目复制到返回缓冲区中。
		s := struct {
			ptr unsafe.Pointer
			siz int
			cap int
		}{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen}
		copy(buf, *(*[]byte)(unsafe.Pointer(&s)))
		buf = buf[reclen:]
		n += reclen
		cnt++
	}
	// 设置输入fd的seek偏移量以记录
	// 我们已经返回了多少个文件。
	_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
	if err != nil {
		return n, err
	}

	return n, nil
}

// 在运行时包（runtime/sys\u darwin.go）
func syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
